/*
 * Copyright 2008 Nils Adermann
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iostream>
#include <unicode/ustring.h>
#include <unicode/schriter.h>
#include "String.h"
#include "UnicodeException.h"

using namespace JSON;

String::String() :
	Value(STRING)
{
	string = new UnicodeString();
}

String::String(const UChar& c) :
	Value(STRING)
{
	string = new UnicodeString(c);
}

String::String(const UChar32& c) :
	Value(STRING)
{
	string = new UnicodeString(c);
}

String::String(const UnicodeString& u) :
	Value(STRING)
{
	string = new UnicodeString(u);
}

String::String(const char* utf8Data, size_t len) :
	Value(STRING)
{
	string = new UnicodeString();
	append(utf8Data, len);
}

String::String(const String& s) :
	Value(s)
{
	string = new UnicodeString(s.toString());
}

String::~String()
{
	delete string;
}

void String::append(const UChar& c)
{
	string->append(c);
}

void String::append(const UChar32& c)
{
	string->append(c);
}

void String::append(const UnicodeString& u)
{
	string->append(u);
}

void String::append(const char* utf8Data, size_t len)
{
	UErrorCode errorCode = U_ZERO_ERROR;
	int32_t stringLength;
	UChar* unicodeArray;

	u_strFromUTF8(NULL, 0, &stringLength, utf8Data, len, &errorCode);
	if (U_FAILURE(errorCode) && errorCode != U_BUFFER_OVERFLOW_ERROR)
	{
		throw UnicodeException(UnicodeString("Converting UTF-8 data to UnicodeString: "), errorCode);
	}
	
	errorCode = U_ZERO_ERROR;
	unicodeArray = new UChar[stringLength+1];
	u_strFromUTF8(unicodeArray, stringLength+1, NULL, utf8Data, len, &errorCode); 
	if (U_FAILURE(errorCode))
	{
		throw UnicodeException(UnicodeString("Converting UTF-8 data to UnicodeString: "), errorCode);
	}

	string->append(unicodeArray, stringLength);
	delete[] unicodeArray;
}

void String::append(const String& s)
{
	string->append(s.toString());
}

UnicodeString& String::toString()
{
	return *string;
}

const UnicodeString& String::toString() const
{
	return *string;
}

void String::toJSON(UnicodeString* u) const
{
	// escape the string
	UChar32 c;
	UnicodeString escaped(string->length(), 0, 0);
	StringCharacterIterator it(*string);
	short noDigit = 0;

	for(c = it.firstPostInc(); c != CharacterIterator::DONE; c = it.nextPostInc())
	{
		if (noDigit) noDigit--;

		switch (c)
		{
			case '\\':
				escaped.append("\\\\");
			break;

			case '"':
				escaped.append("\\\"");
			break;

			case 0:
				escaped.append("\\0");
				noDigit = 2;
			break;

			case '\n':
				escaped.append("\\n");
			break;

			case '\r':
				escaped.append("\\r");
			break;

			case 0x2028:
				escaped.append("\\u2028");
			break;

			case 0x2029:
				escaped.append("\\u2029");
			break;

			/*case '\b':
				escaped.append("\\b");
			break;

			case '\f':
				escaped.append("\\f");
			break;

			case '\t':
				escaped.append("\\t");
			break;

			case '\v':
				escaped.append("\\v");
			break;*/

			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				if (noDigit)
				{
					escaped.append("\\u003"); // 0 is U+0030, 1 is U+0031 etc.
					escaped.append(c);
				}
				else
				{
					escaped.append(c);
				}
			break;

			default:
				escaped.append(c);
			break;
		}
	}

	u->append("\"");
	u->append(escaped);
	u->append("\"");
}

String& String::operator=(const String& s)
{
	if (&s != this)
	{
		delete string;
		string = new UnicodeString(s.toString());
	}
	return *this;
}

bool String::operator==(const Value& v) const
{
	if (getType() == v.getType())
	{
		return *string == v.toString(); 
	}

	return false;
}

bool String::operator!=(const Value& v) const
{
	if (getType() == v.getType())
	{
		return *string != v.toString(); 
	}

	return true;
}

bool String::operator>(const Value& v) const
{
	if (getType() == v.getType())
	{
		return *string > v.toString();
	}

	return getType() > v.getType();
}

bool String::operator<(const Value& v) const
{
	if (getType() == v.getType())
	{
		return *string < v.toString();
	}

	return getType() < v.getType();
}

bool String::operator>=(const Value& v) const
{
	if (getType() == v.getType())
	{
		return *string >= v.toString();
	}

	return getType() >= v.getType();
}

bool String::operator<=(const Value& v) const
{
	if (getType() == v.getType())
	{
		return *string <= v.toString();
	}

	return getType() <= v.getType();
}

